<!--
编辑表单通用组件，有编辑与详情两个模式

列没被配置，则默认占一行

表单项支持插槽模式，
    插槽名为item-${prop} 会返回处理后的field,data值 跟index下标值
    label插槽为item-${prop}-label
    表单项自定义错误插槽为`item-${col.prop}-error

    组件其他插槽： before after

项的el-form-item引用： item-${prop}

配置项: {
    editable
    model
    gutter，
    col // 全局的列配置， 会被field继承
    fields: [
        {
            // el-form-item 常用属性
            prop, // 表单项对应字段
            label,
            rules,

            bind, // el-form-item的特殊属性  除 prop,label,rules以外的
            on, // el-form-item的事件

            // 功能配置属性
            editable, // 该项是否可编辑
            col, // 宽度 总的24
            isBr, // 是否在当前列直接换行
            isHidden, // 是否隐藏
            translator, // 作为详情时字段转换函数
            isHtml, // 作为详情时作为html展示

            // 表单项组件属性
            tag, // 表单项组件 如果是复杂组件，需使用插槽
            tagBind, // 表单项组件的属性
            tagOn // 表单项的事件
        }
    ]
}

-->
<template>
  <el-form class="edit-form" ref="form" :model="model" :label-suffix="labelSuffix" v-bind="$attrs" v-on="$listeners">
    <slot name="before"></slot>

    <el-row :gutter="gutter" v-for="row in rows" :key="row.key">
      <el-col v-for="col in row.cols" :key="col.key" :span="col.col">
        <dg-form-item v-if="!col.isHidden" :prop="col.prop" :ref="`item-${col.prop}`" v-bind="col.bind" v-on="col.on">
          <!-- label插槽 -->
          <slot :name="`item-${col.prop}-label`" slot="label" :data="col" :index="col.index"> </slot>

          <!-- 内容插槽 -->
          <slot :name="`item-${col.prop}`" :data="col" :index="col.index">
            <!--编辑模式-->
            <template v-if="col.tag && col.editable">
              <component :is="col.tag" v-model.trim="model[col.prop]" v-bind="col.tagBind" v-on="col.tagOn">
              </component>
            </template>

            <!--详情模式-->
            <template v-else>
              <span v-if="col.isHtml" class="edit-form__text" v-html="col.translator(model[col.prop])"></span>
              <span
                v-else-if="col.code"
                v-translate="{ code: col.code, value: col.translator(model[col.prop]) }"
              ></span>
              <span v-else class="edit-form__text">{{ col.translator(model[col.prop]) }}</span>
            </template>
          </slot>

          <!-- 异常消息插槽 -->
          <slot :name="`item-${col.prop}-error`" slot="error" :data="col" :index="col.index"></slot>
        </dg-form-item>
      </el-col>
    </el-row>

    <slot name="after"></slot>
  </el-form>
</template>

<script>
import { Form } from 'element-ui';

// 继承el-form的方法
let methods = {};
Object.keys(Form.methods).forEach((key) => {
  methods[key] = function (...args) {
    const vm = this;
    return vm.$refs.form && vm.$refs.form[key](...args);
  };
});

export default {
  name: 'EditForm',
  inheritAttrs: false,
  mixins: [{ methods }],
  props: {
    editable: {
      type: Boolean,
      default: true
    },
    gutter: {
      default: 20
    },
    model: {
      type: Object,
      required: true
    },
    labelSuffix: {
      type: String,
      default: ':'
    },
    fields: {
      type: Array,
      default() {
        return [];
      }
    },
    col: [Number, String] // 全局的列宽度配置
  },
  data() {
    return {
    }
  },
  computed: {
    // 全局col
    golbalCol() {
      return Number.parseInt(this.col) || Number.NaN;
    },
    /**
     * 根据配置，组装成el-row的行配置
     * @return {Array}
     */
    rows() {
      return this.fields2Rows(this.fields);
    }
  },
  methods: {
    /**
     * 表单项配置转成行格式
     * @param fields
     * @return {Array}
     */
    fields2Rows(fields) {
      const vm = this;

      // 整合项属性
      fields = fields.map((field) => vm.mergeFieldAttr(field));

      let rows = [];
      let row = {
        key: '',
        cols: []
      };
      let rowColNum = 0;

      fields.forEach((field, index) => {
        field.index = index;
        row.cols.push(field);
        row.key += field.prop || '';

        // 强制换行，宽度达到一行宽度， 最后一个元素， 剩余不足容纳下一个元素，则换行
        let nextCol = index < fields.length - 1 ? Number.parseInt(fields[index + 1].col) || 24 : 24;
        rowColNum += field.col;

        if (field.isBr || rowColNum >= 24 || index === fields.length - 1 || rowColNum + nextCol > 24) {
          rows.push(row);
          row = {
            key: '',
            cols: []
          };
          rowColNum = 0;
        }
      });

      return rows;
    },

    /**
     * 整合表单项的配置数据
     * @param field
     */
    mergeFieldAttr(field) {
      const vm = this;
      field = Object.assign({}, field); // 扩展一份 避免污染

      // 设置键值
      field.key = field.key || field.prop || `${Math.random()}`;

      if (typeof field.translator !== 'function') {
        field.translator = (value) => value;
      }

      // 整合bind 跟 field里的常用属性
      field.bind = Object.assign(
        {
          label: field.label,
          rules: field.rules
        },
        field.bind
      );

      // 计算该列占多少空间
      field.col = Number.parseInt(field.col) || vm.golbalCol || 24;

      // 整合自身的editable 自己没指定，继承全局的
      if (!field.hasOwnProperty('editable')) {
        field.editable = vm.editable;
      }

      // 添加no-editable class
      if (!field.editable) {
        field.bind.class = field.bind.class || '';
        if (Array.isArray(field.bind.class)) {
          field.bind.class.join(' ');
        }
        field.bind.class += ' no-editable';
      }

      return field;
    }
  },
  activated() {
    this.clearValidate();
  }
};
</script>

<style lang="scss" scoped>
.el-form {
  width: 100%;
  box-sizing: border-box;
}
.el-form-item.no-editable {
  margin-bottom: 0;
}
</style>
